using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using StreetlightGames.SpaceShooter.Enemies;
using StreetlightGames.SpaceShooter.WaveMetaData;
using StreetlightGames.XnaEngine;
using StreetlightGames.XnaEngine.Particles;

namespace StreetlightGames.SpaceShooter
{
    /// <summary>
    /// Implements Microsoft.Xna.Framework.Game.  Contains the main game loop and all game-level logic.
    /// </summary>
    public class SpaceShooterGame : Microsoft.Xna.Framework.Game
    {
        #region Members

        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        private TextRenderer _textRenderer;

        private Starfield _starfield;
        private LevelDisplay _levelDisplay;

        private List<Wave> _waves;
        private int _waveIndex;

        private double _startupDelay = 7;
        private double _elapsedDelay;
        private bool _isPauseKeyDown;

        private GameState _lastState;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the SpaceShooterGame class.
        /// </summary>
        public SpaceShooterGame()
        {
            _graphics = new GraphicsDeviceManager(this);
            _graphics.PreferredBackBufferWidth = 800;
            _graphics.PreferredBackBufferHeight = 450;
            // _graphics.IsFullScreen = true;

            Content.RootDirectory = AppDomain.CurrentDomain.BaseDirectory + "\\Content";
        }

        #endregion

        #region Properties

        /// <summary>Gets the Minimum X value for the player ship</summary>
        public static float MinX
        {
            get { return _minX; }
        }
        private static float _minX = -500;

        /// <summary>Gets the Maximum X value for the player ship</summary>
        public static float MaxX
        {
            get { return _maxX; }
        }
        private static float _maxX = 500;

        /// <summary>Gets the Player value</summary>
        public Player Player
        {
            get { return _player; }
        }
        private Player _player;

        /// <summary>
        /// Gets the current Level.  Note that the current level is not the same as the wave index.
        /// The wave index is zero-based and the level is one-based.  Also, the level does not count
        /// challenge stages.</summary>
        public int Level
        {
            get { return _level; }
        }
        private int _level;

        /// <summary>Gets the Score</summary>
        public int Score
        {
            get { return _score; }
            internal set
            {
                _score = value;
                if (_score > _highScore)
                {
                    _highScore = _score;
                }
            }
        }
        private int _score;

        /// <summary>Gets or sets the HighScore value</summary>
        public int HighScore
        {
            get { return _highScore; }
            internal set { _highScore = value; }
        }
        private int _highScore;

        /// <summary>Gets the ExplosionSystem value</summary>
        public ExplosionParticleSystem ExplosionSystem
        {
            get { return _explosionSystem; }
        }
        private ExplosionParticleSystem _explosionSystem;

        /// <summary>Gets the Camera value</summary>
        public Camera Camera
        {
            get { return _camera; }
        }
        private Camera _camera;

        /// <summary>Gets the State value</summary>
        public GameState State
        {
            get { return _state; }
        }
        private GameState _state;

        /// <summary>Gets the Wave value</summary>
        public Wave Wave
        {
            get { return _wave; }
        }
        private Wave _wave;

        #endregion

        #region Public Methods

        /// <summary>
        /// Loads wave data from the specified collection.
        /// </summary>
        /// <param name="data">Collection of WaveData items to load</param>
        public void LoadWaves(WaveDataCollection data)
        {
            _waves = WaveDataCollection.CreateWaves(this, data);
            if (_waves.Count > 0)
            {
                _wave = _waves[0];
            }
        }

        /// <summary>
        /// Called when a player ship is captured to either spawn a new ship or end the game if no lives are left.
        /// </summary>
        public void CapturePlayerShip()
        {
            if (_player.Lives == 1)
            {
                EndGame();
            }
        }

        #endregion

        #region Overridden Game loop Methods

        /// <summary>
        /// Overridden.  Performs initial setup and initializes all components.
        /// </summary>
        protected override void Initialize()
        {
            Vector3 cameraPosition = new Vector3(0, 1000, 500);
            Vector3 cameraTarget = new Vector3(0.0f, 0.0f, -500.0f);
            _camera = new Camera(
                this,
                cameraPosition,
                cameraTarget,
                Vector3.Up,
                1.0f,
                10000.0f);
            Components.Add(_camera);

            _level = 1;

            _explosionSystem = new XnaEngine.Particles.ExplosionParticleSystem(this);
            _explosionSystem.DrawOrder = 100;
            Components.Add(_explosionSystem);

            _state = GameState.SplashScreen;

            _starfield = new Starfield(this);
            Components.Add(_starfield);

            _levelDisplay = new LevelDisplay(this);
            _levelDisplay.Camera = _camera;
            Components.Add(_levelDisplay);

            if (_waves == null)
            {
                _waves = new List<Wave>();
                WaveData waveData = WaveData.CreateDefault();
                _wave = new Wave(this, waveData);
                _waves.Add(_wave);
                WaveDataCollection waveDataCollection = new WaveDataCollection();
                waveDataCollection.Add(waveData);
            }

            _wave.Camera = _camera;
            Components.Add(_wave);

            _player = new Player(this);
            Components.Add(_player);

            base.Initialize();
        }

        /// <summary>
        /// Overridden.  Loads content for all components and all graphical game content.
        /// </summary>
        protected override void LoadContent()
        {
            base.LoadContent();

            // Create a new SpriteBatch, which can be used to draw textures.
            _spriteBatch = new SpriteBatch(GraphicsDevice);
            _textRenderer = new TextRenderer("Fonts\\Pericles Light");
            _textRenderer.LoadContent(Content, _spriteBatch);

            HighScore = HighScoreData.GetHighScore();

            AudioController.Instance.LoadContent(
                "Content\\Audio\\GameAudio.xgs",
                "Content\\Audio\\Wave Bank.xwb",
                "Content\\Audio\\Sound Bank.xsb");

            _levelDisplay.LoadLevels(this);
        }

        /// <summary>
        /// Overridden.  Called once per cycle in the game loop to update all logic.  Also calls
        /// update for any components.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            if (State != GameState.Paused)
            {
                base.Update(gameTime);
            }

            if (_player.Lives == 0)
            {
                EndGame();
            }

            _elapsedDelay += gameTime.ElapsedGameTime.TotalSeconds;

            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            {
                this.Exit();
                return;
            }

            KeyboardState keyboardState = Keyboard.GetState();

            if (keyboardState.IsKeyDown(Keys.Escape))
            {
                if ((State == GameState.Paused && !_isPauseKeyDown) || State == GameState.GameOver)
                {
                    Exit();
                    return;
                }
                else
                {
                    _state = GameState.Paused;
                    _isPauseKeyDown = true;
                }
            }
            else
            {
                _isPauseKeyDown = false;
                if (keyboardState.IsKeyDown(Keys.F11))
                {
                    _graphics.ToggleFullScreen();
                }
            }

            if (State == GameState.Paused)
            {
                if (keyboardState.IsKeyDown(Keys.Space))
                {
                    _isPauseKeyDown = false;
                    _state = GameState.Playing;
                }
                else
                {
                    return;
                }
            }

            if (keyboardState.IsKeyDown(Keys.Up) && _camera.Position.Y < 1000)
            {
                _camera.Position = new Vector3(_camera.Position.X, _camera.Position.Y + 10, _camera.Position.Z);
            }
            else if (keyboardState.IsKeyDown(Keys.Down) && _camera.Position.Y > 500)
            {
                _camera.Position = new Vector3(_camera.Position.X, _camera.Position.Y - 10, _camera.Position.Z);
            }
            if (State == GameState.Playing)
            {
                if (_wave.IsComplete && _player.State != PlayerState.DoublingUp)
                {
                    AdvanceWave();
                }
            }
            else if (State == GameState.GameOver)
            {
                if (keyboardState.IsKeyDown(Keys.Enter))
                {
                    Restart();
                }

                return;
            }
            else if (State == GameState.BetweenWaves)
            {
                _elapsedDelay += gameTime.ElapsedGameTime.TotalSeconds;
                if (_elapsedDelay > 2)
                {
                    _state = GameState.Starting;
                }
            }
            else if (State == GameState.SplashScreen)
            {
                _lastState = State;
                _state = GameState.Instructions;
            }
            else if (State == GameState.Instructions)
            {
                if (keyboardState.IsKeyDown(Keys.Space))
                {
                    AudioController.Instance.PlayCue("GalagaTheme");
                    if (!string.IsNullOrEmpty(_wave.SoundCue))
                    {
                        AudioController.Instance.PlayCue(_wave.SoundCue);
                    }

                    _state = GameState.BetweenWaves;
                    _elapsedDelay = 0;
                }
            }
            else if (State == GameState.Starting)
            {
                if (_elapsedDelay > _startupDelay)
                {
                    _wave.Start();
                    _player.Start();
                    _state = GameState.Playing;
                }
                else
                {
                    return;
                }
            }
        }

        /// <summary>
        /// Overridden.  Draws all game content and calls Draw for all drawable components.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            _explosionSystem.SetCamera(_camera.ViewMatrix, _camera.ProjectionMatrix);

            if (State == GameState.Paused)
            {
                _textRenderer.DrawStringCentered(
                    "Paused. Press Esc to exit.  Press Space to continue.",
                    new Vector2(_graphics.GraphicsDevice.Viewport.Width / 2, _graphics.GraphicsDevice.Viewport.Height / 2),
                    Color.Red);
            }

            if (State == GameState.Starting)
            {
                if (_elapsedDelay > _startupDelay * 0.9)
                {
                    _textRenderer.DrawStringCentered(
                        "GO!",
                        new Vector2(_graphics.GraphicsDevice.Viewport.Width / 2, _graphics.GraphicsDevice.Viewport.Height / 2),
                        Color.Red);
                }
                else
                {
                    _textRenderer.DrawStringCentered(
                        "Get Ready!",
                        new Vector2(_graphics.GraphicsDevice.Viewport.Width / 2, _graphics.GraphicsDevice.Viewport.Height / 2),
                        Color.Yellow);
                }
            }
            else if (State == GameState.BetweenWaves)
            {
                if (!string.IsNullOrEmpty(_wave.Name))
                {
                    _textRenderer.DrawStringCentered(
                        _wave.Name,
                        new Vector2(_graphics.GraphicsDevice.Viewport.Width / 2, _graphics.GraphicsDevice.Viewport.Height / 2),
                        Color.White);
                }
            }
            else if (State == GameState.Instructions)
            {
                _textRenderer.DrawStringCentered(
                    "Use arrow keys to move left and right.\nPress Space to fire.\nPress Esc to pause.\nPress F11 to toggle full-screen mode.\n\nPress Space to start.",
                    new Vector2(_graphics.GraphicsDevice.Viewport.Width / 2, _graphics.GraphicsDevice.Viewport.Height / 2),
                    Color.Yellow);
            }
            else
            {
                if (State == GameState.GameOver)
                {
                    _textRenderer.DrawStringCentered(
                        "Game Over. Press Enter to start over.  Press Esc to exit.",
                        new Vector2(_graphics.GraphicsDevice.Viewport.Width / 2, _graphics.GraphicsDevice.Viewport.Height / 2),
                        Color.Red);
                }

                _textRenderer.DrawString(string.Format("{0:N0}", Score), new Vector2(10, 10), Color.White);
                _textRenderer.DrawString(string.Format("{0:N0}", HighScore), new Vector2(_graphics.GraphicsDevice.Viewport.Width / 2, 10), Color.White);
            }

            base.Draw(gameTime);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Ends the current game.
        /// </summary>
        private void EndGame()
        {
            _player.State = PlayerState.None;
            _state = GameState.GameOver;
            if (Score == HighScore)
            {
                HighScoreData.Save(null, Score);
            }
        }

        /// <summary>
        /// Advances game to the next wave.
        /// </summary>
        private void AdvanceWave()
        {
            _waveIndex++;
            if (_waveIndex > _waves.Count - 1)
            {
                _waveIndex = 0;
            }

            Components.Remove(_wave);
            _wave = _waves[_waveIndex];

            if (!_wave.IsChallengeStage)
            {
                _level++;
                _levelDisplay.LoadLevels(this);
            }

            _wave.Camera = _camera;
            _wave.Initialize();
            Components.Add(_wave);
            _player.Start();
            _state = GameState.BetweenWaves;
            if (!string.IsNullOrEmpty(_wave.SoundCue))
            {
                AudioController.Instance.PlayCue(_wave.SoundCue);
            }

            _elapsedDelay = 0;
        }

        /// <summary>
        /// Restarts the game.
        /// </summary>
        private void Restart()
        {
            _state = GameState.BetweenWaves;
            _waveIndex = 0;
            _level = 1;
            _player.Restart();
            _score = 0;
            _wave.Stop();
            _levelDisplay.LoadLevels(this);
            Components.Remove(_wave);
            _wave = _waves[_waveIndex];
            Components.Add(_wave);
            _wave.Stop();
            _elapsedDelay = 0;
        }

        #endregion
    }
}
